 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.actions;

 import java.lang.reflect.InvocationTargetException ;
 import java.net.URI ;
 import java.util.List ;

 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.core.filesystem.URIUtil;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IProjectDescription;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dialogs.ProjectLocationSelectionDialog;
 import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
 import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
 import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog;
 import org.eclipse.ui.plugin.AbstractUIPlugin;

 /**
  * The CopyProjectAction is the action designed to copy projects specifically as
  * they have different semantics from other resources. Note that this action
  * assumes that a single project is selected and being manipulated. This should
  * be disabled for multi select or no selection.
  */
 public class CopyProjectAction extends SelectionListenerAction {
     private static String COPY_TOOL_TIP = IDEWorkbenchMessages.CopyProjectAction_toolTip;

     private static String COPY_TITLE = IDEWorkbenchMessages.CopyProjectAction_title;

     private static String PROBLEMS_TITLE = IDEWorkbenchMessages.CopyProjectAction_copyFailedTitle;

     /**
      * The id of this action.
      */
     public static final String ID = PlatformUI.PLUGIN_ID + ".CopyProjectAction";//$NON-NLS-1$

     /**
      * The shell in which to show any dialogs.
      */
     protected Shell shell;

     /**
      * Status containing the errors detected when running the operation or
      * <code>null</code> if no errors detected.
      */
     protected IStatus errorStatus;

     private String [] modelProviderIds;

     /**
      * Creates a new project copy action with the default text.
      *
      * @param shell
      * the shell for any dialogs
      */
     public CopyProjectAction(Shell shell) {
         this(shell, COPY_TITLE);
         PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
                 IIDEHelpContextIds.COPY_PROJECT_ACTION);
     }

     /**
      * Creates a new project copy action with the given text.
      *
      * @param shell
      * the shell for any dialogs
      * @param name
      * the string used as the text for the action, or
      * <code>null</code> if there is no text
      */
     CopyProjectAction(Shell shell, String name) {
         super(name);
         setToolTipText(COPY_TOOL_TIP);
         setId(CopyProjectAction.ID);
         if (shell == null) {
             throw new IllegalArgumentException ();
         }
         this.shell = shell;
     }

     /**
      * Create a new IProjectDescription for the copy using the name and path
      * selected from the dialog.
      *
      * @return IProjectDescription
      * @param project
      * the source project
      * @param projectName
      * the name for the new project
      * @param rootLocation
      * the path the new project will be stored under.
      */
     protected IProjectDescription createDescription(IProject project,
             String projectName, IPath rootLocation) throws CoreException {
         // Get a copy of the current description and modify it
 IProjectDescription newDescription = project.getDescription();
         newDescription.setName(projectName);

         // If the location is the default then set the location to null
 if (rootLocation.equals(Platform.getLocation())) {
             newDescription.setLocation(null);
         } else {
             newDescription.setLocation(rootLocation);
         }

         return newDescription;
     }

     /**
      * Opens an error dialog to display the given message.
      * <p>
      * Note that this method must be called from UI thread.
      * </p>
      *
      * @param message
      * the message
      */
     void displayError(String message) {
         MessageDialog.openError(this.shell, getErrorsTitle(), message);
     }

     /**
      * Return the title of the errors dialog.
      *
      * @return java.lang.String
      *
      * @deprecated As of 3.3, the undoable operation created by this action
      * handles error dialogs.
      */
     protected String getErrorsTitle() {
         return PROBLEMS_TITLE;
     }

     /**
      * Get the plugin used by a copy action
      *
      * @return AbstractUIPlugin
      */
     protected org.eclipse.ui.plugin.AbstractUIPlugin getPlugin() {
         return (AbstractUIPlugin) Platform.getPlugin(PlatformUI.PLUGIN_ID);
     }

     /**
      * Copies the project to the new values.
      *
      * @param project
      * the project to copy
      * @param projectName
      * the name of the copy
      * @param newLocation
      * URI
      * @return <code>true</code> if the copy operation completed, and
      * <code>false</code> if it was abandoned part way
      */
     boolean performCopy(final IProject project, final String projectName,
             final URI newLocation) {
         IRunnableWithProgress op = new IRunnableWithProgress() {
             public void run(IProgressMonitor monitor) {
                 org.eclipse.ui.ide.undo.CopyProjectOperation op = new org.eclipse.ui.ide.undo.CopyProjectOperation(
                         project, projectName, newLocation, getText());
                 op.setModelProviderIds(getModelProviderIds());
                 try {
                     PlatformUI.getWorkbench().getOperationSupport()
                             .getOperationHistory().execute(op, monitor,
                                     WorkspaceUndoUtil.getUIInfoAdapter(shell));
                 } catch (ExecutionException e) {
                     if (e.getCause() instanceof CoreException) {
                         recordError((CoreException)e.getCause());
                     } else {
                         IDEWorkbenchPlugin.log(e.getMessage(), e);
                         displayError(e.getMessage());
                     }
                 }
             }
         };

         try {
             new ProgressMonitorJobsDialog(shell).run(true, true, op);
         } catch (InterruptedException e) {
             return false;
         } catch (InvocationTargetException e) {
             displayError(NLS.bind(
                     IDEWorkbenchMessages.CopyProjectAction_internalError, e
                             .getTargetException().getMessage()));
             return false;
         }

         return true;
     }

     /**
      * Query for a new project name and destination using the parameters in the
      * existing project.
      *
      * @return Object [] or null if the selection is cancelled
      * @param project
      * the project we are going to copy.
      */
     protected Object [] queryDestinationParameters(IProject project) {
         ProjectLocationSelectionDialog dialog = new ProjectLocationSelectionDialog(
                 shell, project);
         dialog.setTitle(IDEWorkbenchMessages.CopyProjectAction_copyTitle);
         dialog.open();
         return dialog.getResult();
     }

     /**
      * Records the core exception to be displayed to the user once the action is
      * finished.
      *
      * @param error
      * a <code>CoreException</code>
      */
     final void recordError(CoreException error) {
         this.errorStatus = error.getStatus();
     }

     /**
      * Implementation of method defined on <code>IAction</code>.
      */
     public void run() {

         errorStatus = null;

         IProject project = (IProject) getSelectedResources().get(0);

         // Get the project name and location in a two element list
 Object [] destinationPaths = queryDestinationParameters(project);
         if (destinationPaths == null) {
             return;
         }

         String newName = (String ) destinationPaths[0];
         URI newLocation = URIUtil.toURI((String ) destinationPaths[1]);

         boolean completed = performCopy(project, newName, newLocation);

         if (!completed) {
             return; // not appropriate to show errors
 }

         // If errors occurred, open an Error dialog
 if (errorStatus != null) {
             ErrorDialog.openError(this.shell, getErrorsTitle(), null,
                     errorStatus);
             errorStatus = null;
         }
     }

     /**
      * The <code>CopyResourceAction</code> implementation of this
      * <code>SelectionListenerAction</code> method enables this action only if
      * there is a single selection which is a project.
      */
     protected boolean updateSelection(IStructuredSelection selection) {
         if (!super.updateSelection(selection)) {
             return false;
         }
         if (getSelectedNonResources().size() > 0) {
             return false;
         }

         // to enable this command there must be one project selected and nothing
 // else
 List selectedResources = getSelectedResources();
         if (selectedResources.size() != 1) {
             return false;
         }
         IResource source = (IResource) selectedResources.get(0);
         if (source instanceof IProject && ((IProject) source).isOpen()) {
             return true;
         }
         return false;
     }

     /**
      * Returns the model provider ids that are known to the client that
      * instantiated this operation.
      *
      * @return the model provider ids that are known to the client that
      * instantiated this operation.
      * @since 3.2
      */
     public String [] getModelProviderIds() {
         return modelProviderIds;
     }

     /**
      * Sets the model provider ids that are known to the client that
      * instantiated this operation. Any potential side effects reported by these
      * models during validation will be ignored.
      *
      * @param modelProviderIds
      * the model providers known to the client who is using this
      * operation.
      * @since 3.2
      */
     public void setModelProviderIds(String [] modelProviderIds) {
         this.modelProviderIds = modelProviderIds;
     }
 }

